#include <glib.h>

gint global_count = 0;
GRWLock *global_rwlock = NULL;
GTimer *timer = NULL;

static gpointer _thread_writer_small_func(gpointer data)
{
    gint i = 0;

    for(i=0;i<5;i++) {
        g_rw_lock_writer_lock(global_rwlock);
        g_print("writer small lock \n");
        global_count += i;
        g_print("writer small global_count: %d elapsed:%f \n", global_count, g_timer_elapsed(timer, NULL));
        g_usleep((GPOINTER_TO_INT(data)*100*1000));
        g_print("writer small unlock \n");
        g_rw_lock_writer_unlock(global_rwlock);
        g_usleep((GPOINTER_TO_INT(data)*100*1000));
    }
}

static gpointer _thread_writer_large_func(gpointer data)
{
    gint i = 0;

    for(i=0;i<5;i++) {
        g_rw_lock_writer_lock(global_rwlock);
        g_print("writer large lock \n");
        global_count += i*100;
        g_print("writer large global_count: %d elapsed:%f \n", global_count, g_timer_elapsed(timer, NULL));
        g_usleep((GPOINTER_TO_INT(data)*100*1000));
        g_print("writer large unlock \n");
        g_rw_lock_writer_unlock(global_rwlock);
        g_usleep((GPOINTER_TO_INT(data)*100*1000));
    }
}

static gpointer _thread_reader1_func(gpointer data)
{
    gint i = 0;

    for(i=0;i<5;i++) {
        g_rw_lock_reader_lock(global_rwlock);
        g_print("reader1 lock \n");
        g_print("reader1 global_count: %d elapsed:%f \n", global_count, g_timer_elapsed(timer, NULL));
        g_usleep((GPOINTER_TO_INT(data)*100*1000));
        g_print("reader1 unlock \n");
        g_rw_lock_reader_unlock(global_rwlock);
        g_usleep((GPOINTER_TO_INT(data)*100*1000));
    }
}

static gpointer _thread_reader2_func(gpointer data)
{
    gint i = 0;

    for(i=0;i<5;i++) {
        g_rw_lock_reader_lock(global_rwlock);
        g_print("reader2 lock \n");
        g_print("reader2 global_count: %d elapsed:%f \n", global_count, g_timer_elapsed(timer, NULL));
        g_usleep((GPOINTER_TO_INT(data)*100*1000));
        g_print("reader2 unlock \n");
        g_rw_lock_reader_unlock(global_rwlock);
        g_usleep((GPOINTER_TO_INT(data)*100*1000));
    }
}

void test_thread_rwlock(void)
{
    GThread *th1,*th2,*th3,*th4;

    timer = g_timer_new();
    g_timer_start(timer);

    global_rwlock = g_new0(GRWLock, 1);

    g_print("\n");

    g_rw_lock_init(global_rwlock);
    th1 = g_thread_new("th1", _thread_writer_small_func, GINT_TO_POINTER(1));
    th2 = g_thread_new("th2", _thread_writer_large_func, GINT_TO_POINTER(2));
    th3 = g_thread_new("th3", _thread_reader1_func, GINT_TO_POINTER(1));
    th4 = g_thread_new("th4", _thread_reader2_func, GINT_TO_POINTER(2));
    g_thread_join(th1);
    g_thread_join(th2);
    g_thread_join(th3);
    g_thread_join(th4);
    g_rw_lock_clear(global_rwlock);
    g_free(global_rwlock);

    g_timer_destroy(timer);
}

gint main(gint argc, gchar **argv)
{
    g_test_init(&argc, &argv, NULL);

    g_test_add_func("/thread/rwlock", test_thread_rwlock);

    return g_test_run();
}